#ifndef EASYASIO_PROTORPC_RPC_CODEC_HPP
#define EASYASIO_PROTORPC_RPC_CODEC_HPP

#include <memory>
#include <functional>
#include <string>

namespace easyasio
{
namespace net
{

class Buffer;
class TcpConnection;
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;

class RpcMessage;

// wire format
//
// Field     Length  Content
//
// size      4-byte  N+8
// "RPC0"    4-byte header
// payload   N-byte
// checksum  4-byte  adler32 of "RPC0"+payload
//
class RpcCodec
{
public:
    enum ErrorCode
    {
        kNoError = 0,
        kInvalidLength,
        kCheckSumError,
        kInvalidNameLen,
        kUnknownMessageType,
        kParseError,
    };

    typedef std::function<void (const TcpConnectionPtr&, const RpcMessage&)> ProtobufMessageCallback;
    typedef std::function<void (const TcpConnectionPtr&, Buffer*, ErrorCode)> ErrorCallback;

    explicit RpcCodec(const ProtobufMessageCallback& messageCb)
    : messageCallback_(messageCb),
      errorCallback_(defaultErrorCallback)
    {
    }

    RpcCodec(const ProtobufMessageCallback& messageCb, const ErrorCallback& errorCb)
    : messageCallback_(messageCb),
      errorCallback_(errorCb)
    {
    }

    static void send(const TcpConnectionPtr& conn, const RpcMessage& message);

    void onMessage(const TcpConnectionPtr& conn, Buffer* buf);

    static const std::string& errorCodeToString(ErrorCode errorCode);
    static ErrorCode parse(const char* buf, int len, RpcMessage* message);
    static int32_t asInt32(const char* buf);

    static void defaultErrorCallback(const TcpConnectionPtr&, Buffer*, ErrorCode);

private:
    ProtobufMessageCallback messageCallback_;
    ErrorCallback errorCallback_;

    const static int kHeaderLen = sizeof(int32_t);
    const static int kMinMessageLen = 2*kHeaderLen; // RPC0 + checkSum
    const static int kMaxMessageLen = 64*1024*1024; // same as codec_stream.h kDefaultTotalBytesLimit
};

}
}

#endif  // EASYASIO_PROTORPC_RPC_CODEC_HPP
